import GLenum from './GLenum';
import ActiveInfo from './ActiveInfo';
import Buffer from './Buffer';
import Framebuffer from './Framebuffer';
import Renderbuffer from './Renderbuffer';
import Texture from './Texture';
import Program from './Program';
import Shader from './Shader';
import ShaderPrecisionFormat from './ShaderPrecisionFormat';
import UniformLocation from './UniformLocation';
import GLmethod from './GLmethod';

const processArray = (array, checkArrayType = false) => {
  function joinArray(arr, sep) {
    let res = '';
    for (let i = 0; i < arr.length; i++) {
      if (i !== 0) {
        res += sep;
      }
      res += arr[i];
    }
    return res;
  }

  let type = 'Float32Array';
  if (checkArrayType) {
    if (array instanceof Uint8Array) {
      type = 'Uint8Array';
    } else if (array instanceof Uint16Array) {
      type = 'Uint16Array';
    } else if (array instanceof Uint32Array) {
      type = 'Uint32Array';
    } else if (array instanceof Float32Array) {
      type = 'Float32Array';
    } else {
      throw new Error('Check array type failed. Array type is ' + typeof array);
    }
  }

  const ArrayTypes = {
    Uint8Array: 1,
    Uint16Array: 2,
    Uint32Array: 4,
    Float32Array: 14
  };
  return ArrayTypes[type] + ',' + btoa(joinArray(array, ','));
};

export default class WebGLRenderingContext {
  // static GBridge = null;

  className = 'WebGLRenderingContext';

  constructor(canvas, type, attrs) {
    this._canvas = canvas;
    this._type = type;
    this._version = 'WebGL 1.0';
    this._attrs = attrs;
    this._map = new Map();

    Object.keys(GLenum).forEach((name) =>
      Object.defineProperty(this, name, {
        value: GLenum[name]
      })
    );
  }

  get canvas() {
    return this._canvas;
  }

  activeTexture = function (textureUnit) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.activeTexture + ',' + textureUnit,
      true
    );
  };

  attachShader = function (progarm, shader) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.attachShader + ',' + progarm.id + ',' + shader.id,
      true
    );
  };

  bindAttribLocation = function (program, index, name) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.bindAttribLocation + ',' + program.id + ',' + index + ',' + name,
      true
    );
  };

  bindBuffer = function (target, buffer) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.bindBuffer + ',' + target + ',' + (buffer ? buffer.id : 0),
      true
    );
  };

  bindFramebuffer = function (target, framebuffer) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.bindFramebuffer +
        ',' +
        target +
        ',' +
        (framebuffer ? framebuffer.id : 0),
      true
    );
  };

  bindRenderbuffer = function (target, renderBuffer) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.bindRenderbuffer +
        ',' +
        target +
        ',' +
        (renderBuffer ? renderBuffer.id : 0),
      true
    );
  };

  bindTexture = function (target, texture) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.bindTexture + ',' + target + ',' + (texture ? texture.id : 0),
      true
    );
  };

  blendColor = function (r, g, b, a) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.blendColor +
        ',' +
        target +
        ',' +
        r +
        ',' +
        g +
        ',' +
        b +
        ',' +
        a,
      true
    );
  };

  blendEquation = function (mode) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.blendEquation + ',' + mode,
      true
    );
  };

  blendEquationSeparate = function (modeRGB, modeAlpha) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.blendEquationSeparate + ',' + modeRGB + ',' + modeAlpha,
      true
    );
  };

  blendFunc = function (sfactor, dfactor) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.blendFunc + ',' + sfactor + ',' + dfactor,
      true
    );
  };

  blendFuncSeparate = function (srcRGB, dstRGB, srcAlpha, dstAlpha) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.blendFuncSeparate +
        ',' +
        srcRGB +
        ',' +
        dstRGB +
        ',' +
        srcAlpha +
        ',' +
        dstAlpha,
      true
    );
  };

  bufferData = function (target, data, usage) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.bufferData +
        ',' +
        target +
        ',' +
        processArray(data, true) +
        ',' +
        usage,
      true
    );
  };

  bufferSubData = function (target, offset, data) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.bufferSubData +
        ',' +
        target +
        ',' +
        offset +
        ',' +
        processArray(data, true),
      true
    );
  };

  checkFramebufferStatus = function (target) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.checkFramebufferStatus + ',' + target
    );
    return Number(result);
  };

  clear = function (mask) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.clear + ',' + mask
    );
    this._canvas._needRender = true;
  };

  clearColor = function (r, g, b, a) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.clearColor + ',' + r + ',' + g + ',' + b,
      true
    );
  };

  clearDepth = function (depth) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.clearDepth + ',' + depth,
      true
    );
  };

  clearStencil = function (s) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.clearStencil + ',' + s
    );
  };

  colorMask = function (r, g, b, a) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.colorMask + ',' + r + ',' + g + ',' + b + ',' + a
    );
  };

  compileShader = function (shader) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.compileShader + ',' + shader.id,
      true
    );
  };

  compressedTexImage2D = function (
    target,
    level,
    internalformat,
    width,
    height,
    border,
    pixels
  ) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.compressedTexImage2D +
        ',' +
        target +
        ',' +
        level +
        ',' +
        internalformat +
        ',' +
        width +
        ',' +
        height +
        ',' +
        border +
        ',' +
        processArray(pixels),
      true
    );
  };

  compressedTexSubImage2D = function (
    target,
    level,
    xoffset,
    yoffset,
    width,
    height,
    format,
    pixels
  ) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.compressedTexSubImage2D +
        ',' +
        target +
        ',' +
        level +
        ',' +
        xoffset +
        ',' +
        yoffset +
        ',' +
        width +
        ',' +
        height +
        ',' +
        format +
        ',' +
        processArray(pixels),
      true
    );
  };

  copyTexImage2D = function (
    target,
    level,
    internalformat,
    x,
    y,
    width,
    height,
    border
  ) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.copyTexImage2D +
        ',' +
        target +
        ',' +
        level +
        ',' +
        internalformat +
        ',' +
        x +
        ',' +
        y +
        ',' +
        width +
        ',' +
        height +
        ',' +
        border,
      true
    );
  };

  copyTexSubImage2D = function (
    target,
    level,
    xoffset,
    yoffset,
    x,
    y,
    width,
    height
  ) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.copyTexSubImage2D +
        ',' +
        target +
        ',' +
        level +
        ',' +
        xoffset +
        ',' +
        yoffset +
        ',' +
        x +
        ',' +
        y +
        ',' +
        width +
        ',' +
        height
    );
  };

  createBuffer = function () {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.createBuffer + ''
    );
    const buffer = new Buffer(result);
    this._map.set(buffer.uuid(), buffer);
    return buffer;
  };

  createFramebuffer = function () {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.createFramebuffer + ''
    );
    const framebuffer = new Framebuffer(result);
    this._map.set(framebuffer.uuid(), framebuffer);
    return framebuffer;
  };

  createProgram = function () {
    const id = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.createProgram + ''
    );
    const program = new Program(id);
    this._map.set(program.uuid(), program);
    return program;
  };

  createRenderbuffer = function () {
    const id = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.createRenderbuffer + ''
    );
    const renderBuffer = new Renderbuffer(id);
    this._map.set(renderBuffer.uuid(), renderBuffer);
    return renderBuffer;
  };

  createShader = function (type) {
    const id = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.createShader + ',' + type
    );
    const shader = new Shader(id, type);
    this._map.set(shader.uuid(), shader);
    return shader;
  };

  createTexture = function () {
    const id = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.createTexture + ''
    );
    const texture = new Texture(id);
    this._map.set(texture.uuid(), texture);
    return texture;
  };

  cullFace = function (mode) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.cullFace + ',' + mode,
      true
    );
  };

  deleteBuffer = function (buffer) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.deleteBuffer + ',' + buffer.id,
      true
    );
  };

  deleteFramebuffer = function (framebuffer) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.deleteFramebuffer + ',' + framebuffer.id,
      true
    );
  };

  deleteProgram = function (program) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.deleteProgram + ',' + program.id,
      true
    );
  };

  deleteRenderbuffer = function (renderbuffer) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.deleteRenderbuffer + ',' + renderbuffer.id,
      true
    );
  };

  deleteShader = function (shader) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.deleteShader + ',' + shader.id,
      true
    );
  };

  deleteTexture = function (texture) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.deleteTexture + ',' + texture.id,
      true
    );
  };

  depthFunc = function (func) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.depthFunc + ',' + func
    );
  };

  depthMask = function (flag) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.depthMask + ',' + Number(flag),
      true
    );
  };

  depthRange = function (zNear, zFar) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.depthRange + ',' + zNear + ',' + zFar,
      true
    );
  };

  detachShader = function (program, shader) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.detachShader + ',' + program.id + ',' + shader.id,
      true
    );
  };

  disable = function (cap) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.disable + ',' + cap,
      true
    );
  };

  disableVertexAttribArray = function (index) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.disableVertexAttribArray + ',' + index,
      true
    );
  };

  drawArrays = function (mode, first, count) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.drawArrays + ',' + mode + ',' + first + ',' + count
    );
    this._canvas._needRender = true;
  };

  drawElements = function (mode, count, type, offset) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.drawElements +
        ',' +
        mode +
        ',' +
        count +
        ',' +
        type +
        ',' +
        offset +
        ';'
    );
    this._canvas._needRender = true;
  };

  enable = function (cap) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.enable + ',' + cap,
      true
    );
  };

  enableVertexAttribArray = function (index) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.enableVertexAttribArray + ',' + index,
      true
    );
  };

  flush = function () {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.flush + ''
    );
  };

  framebufferRenderbuffer = function (
    target,
    attachment,
    textarget,
    texture,
    level
  ) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.framebufferRenderbuffer +
        ',' +
        target +
        ',' +
        attachment +
        ',' +
        textarget +
        ',' +
        (texture ? texture.id : 0) +
        ',' +
        level,
      true
    );
  };

  framebufferTexture2D = function (
    target,
    attachment,
    textarget,
    texture,
    level
  ) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.framebufferTexture2D +
        ',' +
        target +
        ',' +
        attachment +
        ',' +
        textarget +
        ',' +
        (texture ? texture.id : 0) +
        ',' +
        level,
      true
    );
  };

  frontFace = function (mode) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.frontFace + ',' + mode,
      true
    );
  };

  generateMipmap = function (target) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.generateMipmap + ',' + target,
      true
    );
  };

  getActiveAttrib = function (progarm, index) {
    const resultString = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getActiveAttrib + ',' + progarm.id + ',' + index
    );
    const [type, size, name] = resultString.split(',');
    return new ActiveInfo({
      type: Number(type),
      size: Number(size),
      name
    });
  };

  getActiveUniform = function (progarm, index) {
    const resultString = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getActiveUniform + ',' + progarm.id + ',' + index
    );
    const [type, size, name] = resultString.split(',');
    return new ActiveInfo({
      type: Number(type),
      size: Number(size),
      name
    });
  };

  getAttachedShaders = function (progarm) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getAttachedShaders + ',' + progarm.id
    );
    const [type, ...ids] = result;
    return ids.map((id) => this._map.get(Shader.uuid(id)));
  };

  getAttribLocation = function (progarm, name) {
    return WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getAttribLocation + ',' + progarm.id + ',' + name
    );
  };

  getBufferParameter = function (target, pname) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getBufferParameter + ',' + target + ',' + pname
    );
    const [type, res] = getBufferParameter;
    return res;
  };

  getError = function () {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getError + ''
    );
    return result;
  };

  getExtension = function (name) {
    return null;
  };

  getFramebufferAttachmentParameter = function (target, attachment, pname) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getFramebufferAttachmentParameter +
        ',' +
        target +
        ',' +
        attachment +
        ',' +
        pname
    );
    switch (pname) {
      case GLenum.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
        return (
          this._map.get(Renderbuffer.uuid(result)) ||
          this._map.get(Texture.uuid(result)) ||
          null
        );
      default:
        return result;
    }
  };

  getParameter = function (pname) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getParameter + ',' + pname
    );
    switch (pname) {
      case GLenum.VERSION:
        return this._version;
      case GLenum.ARRAY_BUFFER_BINDING: // buffer
      case GLenum.ELEMENT_ARRAY_BUFFER_BINDING: // buffer
        return this._map.get(Buffer.uuid(result)) || null;
      case GLenum.CURRENT_PROGRAM: // program
        return this._map.get(Program.uuid(result)) || null;
      case GLenum.FRAMEBUFFER_BINDING: // framebuffer
        return this._map.get(Framebuffer.uuid(result)) || null;
      case GLenum.RENDERBUFFER_BINDING: // renderbuffer
        return this._map.get(Renderbuffer.uuid(result)) || null;
      case GLenum.TEXTURE_BINDING_2D: // texture
      case GLenum.TEXTURE_BINDING_CUBE_MAP: // texture
        return this._map.get(Texture.uuid(result)) || null;
      case GLenum.ALIASED_LINE_WIDTH_RANGE: // Float32Array
      case GLenum.ALIASED_POINT_SIZE_RANGE: // Float32Array
      case GLenum.BLEND_COLOR: // Float32Array
      case GLenum.COLOR_CLEAR_VALUE: // Float32Array
      case GLenum.DEPTH_RANGE: // Float32Array
      case GLenum.MAX_VIEWPORT_DIMS: // Int32Array
      case GLenum.SCISSOR_BOX: // Int32Array
      case GLenum.VIEWPORT: // Int32Array
      case GLenum.COMPRESSED_TEXTURE_FORMATS: // Uint32Array
      default:
        const [type, ...res] = result.split(',');
        if (res.length === 1) {
          return Number(res[0]);
        } else {
          return res.map(Number);
        }
    }
  };

  getProgramInfoLog = function (progarm) {
    return WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getProgramInfoLog + ',' + progarm.id
    );
  };

  getProgramParameter = function (program, pname) {
    const res = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getProgramParameter + ',' + program.id + ',' + pname
    );

    const [type, result] = res.split(',').map((i) => parseInt(i));

    if (type === 1) {
      return Boolean(result);
    } else if (type === 2) {
      return result;
    } else {
      throw new Error(
        'Unrecongized program paramater ' + res + ', type: ' + typeof res
      );
    }
  };

  getRenderbufferParameter = function (target, pname) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getRenderbufferParameter + ',' + target + ',' + pname
    );
    return result;
  };

  getShaderInfoLog = function (shader) {
    return WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getShaderInfoLog + ',' + shader.id
    );
  };

  getShaderParameter = function (shader, pname) {
    return WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getShaderParameter + ',' + shader.id + ',' + pname
    );
  };

  getShaderPrecisionFormat = function (shaderType, precisionType) {
    const [rangeMin, rangeMax, precision] =
      WebGLRenderingContext.GBridge.callNative(
        this._canvas.id,
        GLmethod.getShaderPrecisionFormat +
          ',' +
          shaderType +
          ',' +
          precisionType
      );
    const shaderPrecisionFormat = new ShaderPrecisionFormat({
      rangeMin: Number(rangeMin),
      rangeMax: Number(rangeMax),
      precision: Number(precision)
    });
    return shaderPrecisionFormat;
  };

  getShaderSource = function (shader) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getShaderSource + ',' + shader.id
    );
    return result;
  };

  getSupportedExtensions = function () {
    return Object.keys({});
  };

  getTexParameter = function (target, pname) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getTexParameter + ',' + target + ',' + pname
    );
    return result;
  };

  getUniformLocation = function (program, name) {
    const id = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getUniformLocation + ',' + program.id + ',' + name
    );
    if (id === -1) {
      return null;
    } else {
      return new UniformLocation(Number(id));
    }
  };

  getVertexAttrib = function (index, pname) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getVertexAttrib + ',' + index + ',' + pname
    );
    switch (pname) {
      case GLenum.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
        return this._map.get(Buffer.uuid(result)) || null;
      case GLenum.CURRENT_VERTEX_ATTRIB: // Float32Array
      default:
        return result;
    }
  };

  getVertexAttribOffset = function (index, pname) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.getVertexAttribOffset + ',' + index + ',' + pname
    );
    return Number(result);
  };

  isBuffer = function (buffer) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.isBuffer + ',' + buffer.id
    );
    return Boolean(result);
  };

  isContextLost = function () {
    return false;
  };

  isEnabled = function (cap) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.isEnabled + ',' + cap
    );
    return Boolean(result);
  };

  isFramebuffer = function (framebuffer) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.isFramebuffer + ',' + framebuffer.id
    );
    return Boolean(result);
  };

  isProgram = function (program) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.isProgram + ',' + program.id
    );
    return Boolean(result);
  };

  isRenderbuffer = function (renderBuffer) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.isRenderbuffer + ',' + renderbuffer.id
    );
    return Boolean(result);
  };

  isShader = function (shader) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.isShader + ',' + shader.id
    );
    return Boolean(result);
  };

  isTexture = function (texture) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.isTexture + ',' + texture.id
    );
    return Boolean(result);
  };

  lineWidth = function (width) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.lineWidth + ',' + width,
      true
    );
  };

  linkProgram = function (program) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.linkProgram + ',' + program.id,
      true
    );
  };

  pixelStorei = function (pname, param) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.pixelStorei + ',' + pname + ',' + Number(param)
    );
  };

  polygonOffset = function (factor, units) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.polygonOffset + ',' + factor + ',' + units
    );
  };

  readPixels = function (x, y, width, height, format, type, pixels) {
    const result = WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.readPixels +
        ',' +
        x +
        ',' +
        y +
        ',' +
        width +
        ',' +
        height +
        ',' +
        format +
        ',' +
        type
    );
    return result;
  };

  renderbufferStorage = function (target, internalFormat, width, height) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.renderbufferStorage +
        ',' +
        target +
        ',' +
        internalFormat +
        ',' +
        width +
        ',' +
        height,
      true
    );
  };

  sampleCoverage = function (value, invert) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.sampleCoverage + ',' + value + ',' + Number(invert),
      true
    );
  };

  scissor = function (x, y, width, height) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.scissor + ',' + x + ',' + y + ',' + width + ',' + height,
      true
    );
  };

  shaderSource = function (shader, source) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.shaderSource + ',' + shader.id + ',' + source
    );
  };

  stencilFunc = function (func, ref, mask) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.stencilFunc + ',' + func + ',' + ref + ',' + mask,
      true
    );
  };

  stencilFuncSeparate = function (face, func, ref, mask) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.stencilFuncSeparate +
        ',' +
        face +
        ',' +
        func +
        ',' +
        ref +
        ',' +
        mask,
      true
    );
  };

  stencilMask = function (mask) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.stencilMask + ',' + mask,
      true
    );
  };

  stencilMaskSeparate = function (face, mask) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.stencilMaskSeparate + ',' + face + ',' + mask,
      true
    );
  };

  stencilOp = function (fail, zfail, zpass) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.stencilOp + ',' + fail + ',' + zfail + ',' + zpass
    );
  };

  stencilOpSeparate = function (face, fail, zfail, zpass) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.stencilOp + ',' + face + ',' + fail + ',' + zfail + ',' + zpass,
      true
    );
  };

  texImage2D = function (...args) {
    WebGLRenderingContext.GBridge.texImage2D(this._canvas.id, ...args);
  };

  texParameterf = function (target, pname, param) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.texParameterf + ',' + target + ',' + pname + ',' + param,
      true
    );
  };

  texParameteri = function (target, pname, param) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.texParameteri + ',' + target + ',' + pname + ',' + param
    );
  };

  texSubImage2D = function (...args) {
    WebGLRenderingContext.GBridge.texSubImage2D(this._canvas.id, ...args);
  };

  uniform1f = function (location, v0) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform1f + ',' + location.id + ',' + v0
    );
  };

  uniform1fv = function (location, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform1fv + ',' + location.id + ',' + processArray(value),
      true
    );
  };

  uniform1i = function (location, v0) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform1i + ',' + location.id + ',' + v0
      // true
    );
  };

  uniform1iv = function (location, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform1iv + ',' + location.id + ',' + processArray(value),
      true
    );
  };

  uniform2f = function (location, v0, v1) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform2f + ',' + location.id + ',' + v0 + ',' + v1,
      true
    );
  };

  uniform2fv = function (location, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform2fv + ',' + location.id + ',' + processArray(value),
      true
    );
  };

  uniform2i = function (location, v0, v1) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform2i + ',' + location.id + ',' + v0 + ',' + v1,
      true
    );
  };

  uniform2iv = function (location, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform2iv + ',' + location.id + ',' + processArray(value),
      true
    );
  };

  uniform3f = function (location, v0, v1, v2) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform3f + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2,
      true
    );
  };

  uniform3fv = function (location, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform3fv + ',' + location.id + ',' + processArray(value),
      true
    );
  };

  uniform3i = function (location, v0, v1, v2) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform3i + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2,
      true
    );
  };

  uniform3iv = function (location, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform3iv + ',' + location.id + ',' + processArray(value),
      true
    );
  };

  uniform4f = function (location, v0, v1, v2, v3) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform4f +
        ',' +
        location.id +
        ',' +
        v0 +
        ',' +
        v1 +
        ',' +
        v2 +
        ',' +
        v3,
      true
    );
  };

  uniform4fv = function (location, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform4fv + ',' + location.id + ',' + processArray(value),
      true
    );
  };

  uniform4i = function (location, v0, v1, v2, v3) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform4i +
        ',' +
        location.id +
        ',' +
        v0 +
        ',' +
        v1 +
        ',' +
        v2 +
        ',' +
        v3,
      true
    );
  };

  uniform4iv = function (location, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniform4iv + ',' + location.id + ',' + processArray(value, true),
      true
    );
  };

  uniformMatrix2fv = function (location, transpose, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniformMatrix2fv +
        ',' +
        location.id +
        ',' +
        Number(transpose) +
        ',' +
        processArray(value),
      true
    );
  };

  uniformMatrix3fv = function (location, transpose, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniformMatrix3fv +
        ',' +
        location.id +
        ',' +
        Number(transpose) +
        ',' +
        processArray(value),
      true
    );
  };

  uniformMatrix4fv = function (location, transpose, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.uniformMatrix4fv +
        ',' +
        location.id +
        ',' +
        Number(transpose) +
        ',' +
        processArray(value),
      true
    );
  };

  useProgram = function (progarm) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.useProgram + ',' + progarm.id + '',
      true
    );
  };

  validateProgram = function (program) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.validateProgram + ',' + program.id,
      true
    );
  };

  vertexAttrib1f = function (index, v0) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.vertexAttrib1f + ',' + index + ',' + v0,
      true
    );
  };

  vertexAttrib2f = function (index, v0, v1) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.vertexAttrib2f + ',' + index + ',' + v0 + ',' + v1,
      true
    );
  };

  vertexAttrib3f = function (index, v0, v1, v2) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.vertexAttrib3f + ',' + index + ',' + v0 + ',' + v1 + ',' + v2,
      true
    );
  };

  vertexAttrib4f = function (index, v0, v1, v2, v3) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.vertexAttrib4f +
        ',' +
        index +
        ',' +
        v0 +
        ',' +
        v1 +
        ',' +
        v2 +
        ',' +
        v3,
      true
    );
  };

  vertexAttrib1fv = function (index, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.vertexAttrib1fv + ',' + index + ',' + processArray(value),
      true
    );
  };

  vertexAttrib2fv = function (index, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.vertexAttrib2fv + ',' + index + ',' + processArray(value),
      true
    );
  };

  vertexAttrib3fv = function (index, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.vertexAttrib3fv + ',' + index + ',' + processArray(value),
      true
    );
  };

  vertexAttrib4fv = function (index, value) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.vertexAttrib4fv + ',' + index + ',' + processArray(value),
      true
    );
  };

  vertexAttribPointer = function (
    index,
    size,
    type,
    normalized,
    stride,
    offset
  ) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.vertexAttribPointer +
        ',' +
        index +
        ',' +
        size +
        ',' +
        type +
        ',' +
        Number(normalized) +
        ',' +
        stride +
        ',' +
        offset,
      true
    );
  };

  viewport = function (x, y, width, height) {
    WebGLRenderingContext.GBridge.callNative(
      this._canvas.id,
      GLmethod.viewport + ',' + x + ',' + y + ',' + width + ',' + height,
      true
    );
  };
}
